'''
文献标题爬取函数 
配置文件： conf.json 配置期刊名称 发表时间 刊号
输出: paper-info-20210101.txt 当前日期爬取的信息 每列用,隔开
'''
import requests
from urllib.parse import urlencode
from bs4 import BeautifulSoup
import time
import os
import math
import json


'''
网页爬取函数
输入: paper_url 网址
输出: html_content 网页内容
'''
def get_one_page(paper_url):
    # 请求头
    url_headers = {
        'Host': 'www.wanfangdata.com.cn',
        'Referer': 'https://www.wanfangdata.com.cn/sns/perio/wlxb/?tabId=article&publishYear=2021&issueNum=15&page=1&isSync=0',
        'Content-Type': 'text/html;charset=utf-8',
        'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.9 Safari/537.36'
        } 
        
    try:
        response = requests.get(url=paper_url, timeout=5, headers=url_headers)
        time.sleep(1)  # 增加延时 避免ip被封
        if response.status_code == 200:
            return response.text
        else:
            return None
        
    except requests.ConnectionError:
        return None


'''
html文本解析函数
输入: html_content html文本
输出: journal_dict 期刊信息的字典
'''
def parse_one_page(html_content):
    soup = BeautifulSoup(html_content, 'lxml')
    # 解析数据
    foo = soup.find('title')  # 期刊名称
    title = foo.string        
    foo = soup.find('wf-inline')  # 卷号
    vol = foo.string 
    # 解析标题和作者   
    a = list()  # 文章标题
    b = list()  # 文章作者
    foo = soup.find_all('wf-article-item')
    for article in foo:
        c = article.find('h2')
        a.append(c.text if c else '无' ) 
        c = article.find(attrs={'class': 'd-inline-block w-135 fs-12 text-ellipsis align-middle'})
        b.append(c.text if c else '无' )  
    # 构造字典
    journal_dict = {'title': title,
                    'vol': vol,
                    'paper': a,
                    'author': b}

    return journal_dict

'''
解析文献数量函数 根据数量决定翻页次数
输入: html_content html文本
输出: page_cnt 总计页数 如果为0则表示无该卷号
'''
def parse_page_cnt(html_content):
    soup = BeautifulSoup(html_content, 'lxml')
    foo = soup.find('wf-inline-block')    
    
    if foo == None:  # 没有该tag 表示无该卷号
        page_cnt = None
        paper_total = 0        
    else:
        paper_total = int(foo.string)  # 获取总文章数量
        page_cnt = math.ceil(paper_total/10)  # 向上取整
        
    return page_cnt, paper_total


'''
数据存储函数
输入: filename 文件名 , dict 存放期刊信息的字典
输出: 无
'''
def save_to_csv(filename, dict):
    if os.path.exists(filename):  #判断文件是否存在
        pass  
    else:  # 如不存在，新建文件并加入表头
        with open(filename, 'a+', encoding='utf-8') as f:
            f.write(f'期刊名称\t卷号\t论文题目\t作者\n')
    # 写入数据
    with open(filename, 'a+', encoding='UTF-8') as f:
        for i in range(len(dict['paper'])):
            a = dict['title']
            b = dict['vol']
            c = dict['paper'][i]
            d = dict['author'][i]
            f.write(f'{a}\t{b}\t{c}\t{d}\n')

'''
获取某期刊一期函数 先根据期号获取页数 再爬取每一页并解析 存储
输入: journal_url_id 该期刊在网址中的代号 str类型 例如物理学报为 'wlxb'
    publishYear 出版年份 int类型
    issueNum 期号或者卷号 int类型
输出: None 表示失败 或者爬取结束
    'done' 表示正常完成
'''
def get_parse_one_volume(journal_url_id, publishYear, issueNum):
    # 获取当前时间
    now = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))
    # 网页参数字典 用于生成网页参数 xxx=yy&aaa=b 的形式
    url_params = {  
        'tabId': 'article',
        'publishYear': f'{publishYear}',
        'issueNum': f'{issueNum}',
        'page': '1',
        'isSync': '0'
    }
    # 构造网址
    paper_url = f'https://www.wanfangdata.com.cn/sns/perio/{journal_url_id}/?{urlencode(url_params)}'
    # 获取网页
    html_text = get_one_page(paper_url)
    # 爬取网站首页
    if html_text == None:
        print(f'{now} 爬取{publishYear}年第{issueNum}期时失败')
        return None
    page_cnt, paper_tatal = parse_page_cnt(html_text)  # 解析该卷文章数量 获得翻页次数
    if page_cnt == None :  # 没有该卷号
        print(f'{now} 没有爬到{publishYear}年第{issueNum}期')
        return None
    # 爬取每一页
    for page_num in range(1, page_cnt+1):
        # 构造网址
        url_params['page'] = f'{page_num}'
        paper_url = f'https://www.wanfangdata.com.cn/sns/perio/{journal_url_id}/?{urlencode(url_params)}'
        # 获取网页
        html_text = get_one_page(paper_url)  
        if html_text == None:
            print(f'{now} 获取文章标题时网页爬取失败')
            return None
        # 解析网页
        journal_dict = parse_one_page(html_text)
        file_date = time.strftime('%Y%m%d', time.localtime(time.time()))  
        save_to_csv(f'paper-info-{file_date}.txt', journal_dict)
        
        print(f'{now} {paper_tatal}篇文献 共计{page_cnt}页 完成第{page_num}页爬取和解析')
    
    return 'done'


'''
获取一个期刊各期号文章 根据信息字典选择期刊号 起始和结束期号 调用单期函数爬取
输入: j_dict 期刊信息字典 与conf.json含义一致
输出: 如果异常则返回None 否则返回done
'''
def get_parse_one_journal(j_dict):
    
    if j_dict['end_vol'] < j_dict['start_vol']:
        print(f"{j_dict['title']} 期号配置不正确")
        return None
    
    for i in range(j_dict['start_vol'], j_dict['end_vol']+1):
        print(f"{j_dict['title']} 2021年 第{i}期")
        foo = get_parse_one_volume(j_dict['url_id'], j_dict['publishYear'], i)  
        
    return 'done'
    
    
# -----------------以下为主函数
# 导入json配置文件
with open('conf.json', 'r',encoding='utf-8') as f:
    journal = json.load(f)
# 根据配置文件爬取数据
for j_info in journal:
    get_parse_one_journal(j_info)


print('end')